/*    file: main.cpp
 *    desc:
 * 
 * created: 2016-04-06
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include <stdio.h>
#include <iostream>

#include "BasicType.h"
#include "LibMediaCaster.h"

#include "Ffmpeg.h"
#include "SharedPtr.h"
#include "TFileUtil.h"
#include <assert.h>


extern "C"
{
#include "libavutil/time.h"
}


static bool hasPrefix(const uint8_t* data, int size)
{
    assert(size >= 4);
    if ((data[0] == 0) && (data[1] == 0))
    {
        if ((data[2] == 0) && (data[3] == 1))
        {
            return true;
        }
        if (data[2] == 1)
        {
            return true;
        }
    }
    return false;
}

struct CasterGuard
{
public:
    CasterGuard()
    {
        mcaster_init();

        mcaster_start(MCASTER_PROTOCOL_TCP, "", 7000, "");
        mcaster_start(MCASTER_PROTOCOL_UDT, "", 8000, "");
    }

    ~CasterGuard()
    {
        mcaster_quit();
    }
};


int main(int argc, char** argv)
{
	av_register_all();
    avformat_network_init();
	
	if (argc <= 1)
    {
        return EINVAL;
    }

    char* filename = argv[1];
    AVFormatContext* fmtContext = 0;
    int rc = avformat_open_input(&fmtContext, filename, NULL, NULL);
    if (rc != 0)
    {
        return EBADF;
    }

    rc = avformat_find_stream_info(fmtContext, NULL);

    CasterGuard casterGuard;

    MFormat fmt;
    memset(&fmt, 0, sizeof(fmt));
    
    int videoIndex = -1;
    AVCodecContext* vContext = 0;
    AVCodec* videoCodec = NULL;
    {
        int idx  = av_find_best_stream(fmtContext, AVMEDIA_TYPE_VIDEO, -1, -1, &videoCodec, 0);
        if (videoCodec)
        {
            AVStream* stream = fmtContext->streams[idx];
            vContext = stream->codec;
            fmt.width = vContext->width;
            fmt.height = vContext->height;
            fmt.codec = vContext->codec_id;
            fmt.profile = vContext->profile;
            fmt.clockRate = vContext->time_base.den / vContext->time_base.num;
            
            fmt.vPropSize = vContext->extradata_size;
            fmt.vProp = vContext->extradata;

            if (stream->avg_frame_rate.den > 0)
            {
                fmt.framerate = stream->avg_frame_rate.num / stream->avg_frame_rate.den;
            }
            else if (stream->r_frame_rate.den > 0)
            {
                fmt.framerate = stream->r_frame_rate.num / stream->r_frame_rate.den;
            }
            else 
            {
                fmt.framerate = 25;
            }

            videoIndex = idx;

        }

    }
    
    int audioIndex = -1;
    AVCodecContext* aContext = 0;
    AVCodec* audioCodec = NULL;
    {
        int idx = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &audioCodec, 0);
        if (audioCodec)
        {
            AVStream* stream = fmtContext->streams[idx];
            aContext = stream->codec;

            fmt.audioCodec = aContext->codec_id;
            fmt.sampleRate = aContext->sample_rate;
            fmt.channels = aContext->channels;
            //fmt.audioProfile = aContext->profile;
            
            fmt.sampleRate = stream->time_base.den;
            fmt.audioRate = fmt.sampleRate;

            audioIndex = idx;
        }
    }
    
    HANDLE handle = NULL;
    int ret = mcaster_open(&handle, "demo", &fmt);

    AVPacket pkt;
    av_init_packet(&pkt);

    int64_t tmStart = av_gettime_relative();
    int64_t videoDuration = 0;
    int64_t audioDuration = 0;

    while (true)
    {
        rc = av_read_frame(fmtContext, &pkt);
        if (rc != 0)
        {
            av_seek_frame(fmtContext, -1, 0, 0);
            std::cin.get();
            continue;
        }

        int delay = 0;
        if (pkt.stream_index == videoIndex)
        {
            AVStream* stream = fmtContext->streams[pkt.stream_index];
            
            AVRational avbase = av_make_q(1, AV_TIME_BASE);
            int duration = av_rescale_q(pkt.duration, stream->time_base, avbase);
            videoDuration += duration;

            av_packet_rescale_ts(&pkt, stream->time_base, avbase);
            
            int64_t clockDuration = av_gettime_relative() - tmStart;
            delay = videoDuration - clockDuration;
            
            if (!hasPrefix(pkt.data, pkt.size))
            {
                *(int*)pkt.data = 0x01000000;
            }

            int64_t pts = pkt.pts;
            if (pts == AV_NOPTS_VALUE)
            {
                pts = av_rescale_q((tmStart + videoDuration), avbase, avbase);
            }

            MPacket mpkt;
            mpkt.type = MTYPE_VIDEO;
            mpkt.data = pkt.data;
            mpkt.size = pkt.size;
            mpkt.pts = pts;
            mpkt.duration = pkt.duration;
            mpkt.flags = pkt.flags;

            mcaster_write(handle, &mpkt);

            //comn::FileUtil::write(pkt.data, pkt.size, "m.hevc", true);
            //printf("video size:%d, duration:%d, pts:%I64d\n", pkt.size, pkt.duration, pkt.pts);
        }
        else if (pkt.stream_index == audioIndex)
        {
            AVStream* stream = fmtContext->streams[pkt.stream_index];
            //int64_t pts = av_rescale_q(pkt.dts, stream->time_base, timebase);
            AVRational avbase = av_make_q(1, AV_TIME_BASE);
            int duration = av_rescale_q(pkt.duration, stream->time_base, avbase);
            audioDuration += duration;
            int64_t clockDuration = av_gettime_relative() - tmStart;
            delay = audioDuration - clockDuration;

            av_packet_rescale_ts(&pkt, stream->time_base, avbase);

            MPacket mpkt;
            mpkt.type = MTYPE_AUDIO;
            mpkt.data = pkt.data;
            mpkt.size = pkt.size;
            mpkt.pts = pkt.pts;
            mpkt.duration = pkt.duration;
            mpkt.flags = pkt.flags;

            mcaster_write(handle, &mpkt);
        }

        av_free_packet(&pkt);

        //printf("delay: %f ms\n", delay / 1000.0);
        if (delay > 0)
        {
            //av_usleep(delay);
        }
        
    }

    std::cin.get();

    mcaster_close(handle);

    avformat_close_input(&fmtContext);

	return 0;
}




